package com.zyk.scaffold.user.rpc;

import com.zyk.scaffold.common.enums.TenantUserTypeEnum;
import com.zyk.scaffold.common.enums.UserTypeEnum;
import com.zyk.scaffold.common.utils.BeanCopyUtil;
import com.zyk.scaffold.common.utils.MapUtils;
import com.zyk.scaffold.common.utils.StringUtils;
import com.zyk.scaffold.user.domain.FunctionMessage;
import com.zyk.scaffold.user.dto.TenantUserInfoDTO;
import com.zyk.scaffold.user.dto.UserBasicInfoDTO;
import com.zyk.scaffold.user.dto.UserInfoDTO;
import com.zyk.scaffold.user.entity.*;
import com.zyk.scaffold.user.service.*;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.*;
import java.util.stream.Collectors;

@DubboService
public class UserRpcServiceImpl implements UserRpcService{

    @Autowired
    private UserService userService;

    @Autowired
    private TenantUserService tenantUserService;

    @Autowired
    private TenantFunctionService tenantFunctionService;

    @Autowired
    private TenantService tenantService;

    @Autowired
    private TenantRoleService tenantRoleService;

    @Autowired
    private TenantUserRoleService tenantUserRoleService;

    @Autowired
    private FunctionService functionService;

    @Autowired
    private TenantRoleFunctionService tenantRoleFunctionService;

    @Autowired
    private FunctionApiService functionApiService;

    @Override
    public Boolean updateUserBasicInfo(UserBasicInfoDTO userBasicInfoDTO) {
        User user = userService.getUserByAccount(userBasicInfoDTO.getAccount());
        if(user == null){
            return false;
        }
        if(StringUtils.isNotEmpty(userBasicInfoDTO.getPhone())){
            user.setPhone(userBasicInfoDTO.getPhone());
        }
        if(StringUtils.isNotEmpty(userBasicInfoDTO.getName())){
            user.setName(userBasicInfoDTO.getName());
        }
        return userService.updateById(user);
    }

    @Override
    public TenantUserInfoDTO getTenantUserApis(String account, String tenantCode) {
        User user = userService.getUserByAccount(account);
        if(user == null){
            return null;
        }
        TenantUserInfoDTO tenantUserInfoDTO = BeanCopyUtil.copyProperties(user, TenantUserInfoDTO::new);
        Long userId = tenantUserInfoDTO.getId();
        // 获取功能列表
        Map<Long, String> functionId2Code = new HashMap<>();

        if(UserTypeEnum.USER_TYPE_INNER.getCode().equals(user.getType())){
            List<Tenant> tenants = tenantService.getTenantInfos();
            Tenant tenant = selectTenant(tenants, tenantCode);
            if(tenant == null){
                return null;
            }
            tenantCode = tenant.getCode();
            tenantUserInfoDTO.setTenantCode(tenant.getCode());
            tenantUserInfoDTO.setTenantName(tenant.getName());
            tenantUserInfoDTO.setRoles(Collections.emptyList());
            List<Function> functions = tenantFunctionService.getFunctionsByTenantCode(tenantCode);
            functionId2Code.putAll(functions.stream().collect(Collectors.toMap(Function::getId, Function::getCode, (a, b) -> a)));
            tenantUserInfoDTO.setRoles(Arrays.asList("SYSTEM_INNER_ADMIN"));
        }else{
            List<Tenant> tenants = tenantUserService.getTenantInfos(userId);
            Tenant tenant = selectTenant(tenants, tenantCode);
            if(tenant == null){
                return null;
            }
            tenantCode = tenant.getCode();
            tenantUserInfoDTO.setTenantCode(tenant.getCode());
            tenantUserInfoDTO.setTenantName(tenant.getName());

            TenantUser tenantUser = tenantUserService.getTenantUserInfo(tenantCode, userId);
            if(tenantUser == null){
                return tenantUserInfoDTO;
            }
            if(TenantUserTypeEnum.TENANT_USER_TYPE_INNER.getCode().equals(tenantUser.getType())){
                List<Function> functions = tenantFunctionService.getFunctionsByTenantCode(tenantCode);
                functionId2Code.putAll(functions.stream().collect(Collectors.toMap(Function::getId, Function::getCode, (a, b) -> a)));
                tenantUserInfoDTO.setRoles(Collections.emptyList());
                tenantUserInfoDTO.setRoles(Arrays.asList("TENANT_INNER_ADMIN"));
            }else{
                Long tenantUserId = tenantUser.getId();
                List<TenantUserRole> tenantUserRoles = tenantUserRoleService.getUserRolesByUserId(tenantUserId);
                List<Long> roleIds = tenantUserRoles.stream().map(TenantUserRole::getTenantRoleId).filter(Objects::nonNull).distinct().collect(Collectors.toList());
                if(CollectionUtils.isEmpty(roleIds)){
                    return tenantUserInfoDTO;
                }
                List<TenantRole> tenantRoles = tenantRoleService.getRolesByIds(roleIds, tenantCode);
                List<String> roleNames = tenantRoles.stream().map(TenantRole::getName).distinct().collect(Collectors.toList());
                tenantUserInfoDTO.setRoles(roleNames);
                List<TenantRoleFunction> tenantRoleFunctions = tenantRoleFunctionService.getRoleFunctionsByRoleIds(roleIds);
                List<Long> tenantFunctionIds = tenantRoleFunctions.stream().map(TenantRoleFunction::getTenantFunctionId).collect(Collectors.toList());
                if(CollectionUtils.isEmpty(tenantFunctionIds)){
                    return tenantUserInfoDTO;
                }
                List<FunctionMessage> functionMessages = tenantFunctionService.queryTenantCodesFunction(Arrays.asList(tenantCode));
                functionId2Code.putAll(functionMessages.stream()
                        .filter(item -> tenantFunctionIds.contains(item.getTenantFunctionId()))
                        .collect(Collectors.toMap(FunctionMessage::getId, FunctionMessage::getCode, (a, b) -> a)));
            }
        }
        List<Long> functionIds = new ArrayList<>(functionId2Code.keySet());
        if(CollectionUtils.isEmpty(functionIds)){
            return tenantUserInfoDTO;
        }
        List<FunctionApi> functionApis = functionApiService.getFunctionApisByFunctionIds(functionIds);

        Map<Long, Set<String>> mapFunctionId2Apis = functionApis.stream()
                .collect(Collectors.groupingBy(FunctionApi::getFunctionId, Collectors.mapping(FunctionApi::getApiUrl, Collectors.toSet())));
        List<Map<String, Object>> menuApis = functionIds.stream().map(functionId -> {
            Map<String, Object> menuApi = new HashMap<>();
            menuApi.put("code", functionId2Code.get(functionId));
            Set<String> apis = mapFunctionId2Apis.get(functionId);
            if (apis == null) {
                apis = Collections.emptySet();
            }
            menuApi.put("apis", apis);
            return menuApi;
        }).collect(Collectors.toList());
        tenantUserInfoDTO.setMenuApis(menuApis);

        List<FunctionMessage> functionMessages = tenantFunctionService.queryTenantCodesFunction(Arrays.asList(tenantCode));
        List<Map<String, Object>> menuFunctions = functionMessages.stream()
                .filter(item -> functionIds.contains(item.getId()))
                .map(item -> {
                    Map<String, Object> menuFunction = new HashMap<>();
                    menuFunction.put("name", item.getName());
                    menuFunction.put("code", item.getCode());
                    menuFunction.put("parentCode", item.getParentCode());
                    menuFunction.put("uiProperties", new HashMap<>());
                    menuFunction.put("dataProperties", new HashMap<>());
                    menuFunction.put("type", item.getType());
                    return menuFunction;
                })
                .collect(Collectors.toList());
        menuFunctions = buildTreeMenuFunctions(menuFunctions, null, Boolean.TRUE);
        tenantUserInfoDTO.setMenuFunctions(menuFunctions);
        return tenantUserInfoDTO;
    }

    private List<Map<String, Object>> buildTreeMenuFunctions(List<Map<String, Object>> menuFunctions, String parentCode, Boolean isRoot) {
        List<Map<String, Object>> list = new ArrayList<>();
        for (Map<String, Object> menuFunction : menuFunctions) {
            String parentCodeItem = MapUtils.getString(menuFunction, "parentCode");
            if(isRoot){
                if(parentCodeItem == null){
                    String code = MapUtils.getString(menuFunction, "code");
                    List<Map<String, Object>> maps = buildTreeMenuFunctions(menuFunctions, code, Boolean.FALSE);
                    menuFunction.put("childrens", maps);
                    list.add(menuFunction);
                }
            }else{
                if(parentCode.equals(parentCodeItem)){
                    String code = MapUtils.getString(menuFunction, "code");
                    List<Map<String, Object>> maps = buildTreeMenuFunctions(menuFunctions, code, Boolean.FALSE);
                    menuFunction.put("childrens", maps);
                    list.add(menuFunction);
                }
            }
        }
        return list;
    }

    private Tenant selectTenant(List<Tenant> tenants, String tenantCode) {
        if(CollectionUtils.isEmpty(tenants)){
            return null;
        }
        if(StringUtils.isEmpty(tenantCode)){
            return tenants.stream().findFirst().orElse(null);
        }
        return tenants.stream().filter(item -> item.getCode().equals(tenantCode)).findFirst().orElse(null);
    }

    @Override
    public UserInfoDTO getUserByUserId(Long userId) {
        User user = userService.getById(userId);
        if(user == null){
            return null;
        }
        UserInfoDTO userInfoDTO = BeanCopyUtil.copyProperties(user, UserInfoDTO::new);
        return userInfoDTO;
    }
}
